﻿/*
 * RegExpress
 * 
 * Copyright (c) 2010, Daniel McGaughran
 * 
 * Licensed under the Apache Licence, Version 2.0 (the "Licence");
 * you may not use this file except in compliance with the Licence.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 * 
 */

using System;
using System.Windows.Controls;
using System.Windows;


namespace XrControls
{
	public class UniformDoubleStack : Panel
	{
		public static readonly DependencyProperty OrientationProperty;
		public static readonly DependencyProperty AllowNativeSizeProperty;
		public static readonly DependencyProperty IsAtStackEndProperty;

		private double _maxChildLength;
		private int _numUniformElements;
		private Size _desiredSize;


		static UniformDoubleStack()
		{
			OrientationProperty = DependencyProperty.Register("Orientation",
					typeof(Orientation), typeof(UniformDoubleStack), new FrameworkPropertyMetadata
					(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure
					| FrameworkPropertyMetadataOptions.AffectsArrange));

			AllowNativeSizeProperty = DependencyProperty.RegisterAttached("AllowNativeSize",
					typeof(bool), typeof(UniformDoubleStack), new FrameworkPropertyMetadata
					(false, FrameworkPropertyMetadataOptions.AffectsMeasure
					| FrameworkPropertyMetadataOptions.AffectsArrange));

			IsAtStackEndProperty = DependencyProperty.RegisterAttached("IsAtStackEnd",
					typeof(bool), typeof(UniformDoubleStack), new FrameworkPropertyMetadata
					(false, FrameworkPropertyMetadataOptions.AffectsMeasure
					| FrameworkPropertyMetadataOptions.AffectsArrange));

		}



		public static void SetAllowNativeSize(UIElement element, bool value)
		{
			element.SetValue(AllowNativeSizeProperty, value);
		}
		public static bool GetAllowNativeSize(UIElement element)
		{
			return (bool)element.GetValue(AllowNativeSizeProperty);
		}



		public static void SetIsAtStackEnd(UIElement element, bool value)
		{
			element.SetValue(IsAtStackEndProperty, value);
		}
		public static bool GetIsAtStackEnd(UIElement element)
		{
			return (bool)element.GetValue(IsAtStackEndProperty);
		}



		/// <summary>
		/// Gets or sets the orientation of this panel.
		/// </summary>
		/// <value>The orientation.</value>
		public Orientation Orientation
		{
			get { return (Orientation)GetValue(OrientationProperty); }
			set { SetValue(OrientationProperty, value); }
		}



		protected override Size MeasureOverride(Size availableSize)
		{
			//Count uniform items
			_numUniformElements = 0;
			foreach (UIElement currentElement in base.InternalChildren)
			{
				if (!GetAllowNativeSize(currentElement))
					_numUniformElements++;
			}

			Size uniformConstraint = new Size(availableSize.Width, availableSize.Height);
			bool isHorizontal = Orientation == Orientation.Horizontal;
			if (isHorizontal)
			{
				if (_numUniformElements > 0 && availableSize.Width != Double.PositiveInfinity)
					uniformConstraint.Width = availableSize.Width / _numUniformElements;
			}
			else
			{
				if (_numUniformElements > 0 && availableSize.Height != Double.PositiveInfinity)
					uniformConstraint.Height = availableSize.Height / _numUniformElements;
			}

			//Recursively measure children
			_maxChildLength = 0.0;
			_desiredSize = new Size();
			foreach (UIElement currentElement in base.InternalChildren)
			{
				Size childSize = (GetAllowNativeSize(currentElement)) ? availableSize : uniformConstraint;
				currentElement.Measure(childSize);

				if (isHorizontal)
				{
					if(GetAllowNativeSize(currentElement))
						_desiredSize.Width += currentElement.DesiredSize.Width;
					else
						_maxChildLength = Math.Max(_maxChildLength, currentElement.DesiredSize.Width);

					_desiredSize.Height = Math.Max(_desiredSize.Height, currentElement.DesiredSize.Height);
				}
				else
				{
					_desiredSize.Width = Math.Max(_desiredSize.Width, currentElement.DesiredSize.Width);

					if (GetAllowNativeSize(currentElement))
						_desiredSize.Height += currentElement.DesiredSize.Height;
					else
						_maxChildLength = Math.Max(_maxChildLength, currentElement.DesiredSize.Height);
				}
			}

			if (isHorizontal)
				_desiredSize.Width += _numUniformElements * _maxChildLength;
			else
				_desiredSize.Height += _numUniformElements * _maxChildLength;

			return _desiredSize;
		}



		protected override Size ArrangeOverride(Size finalSize)
		{
			bool isHorizontal = Orientation == Orientation.Horizontal;
			Point currentPosition = new Point();

			foreach (UIElement currentElement in base.InternalChildren)
			{
				if (GetIsAtStackEnd(currentElement)) continue;

				Size childSize = new Size(finalSize.Width, finalSize.Height);
				if (isHorizontal)
					childSize.Width = (GetAllowNativeSize(currentElement)) ?
						currentElement.DesiredSize.Width : _maxChildLength;
				else
					childSize.Height = (GetAllowNativeSize(currentElement)) ?
						currentElement.DesiredSize.Height : _maxChildLength;

				Rect childBounds = new Rect(currentPosition, childSize);
				currentElement.Arrange(childBounds);

				if (!isHorizontal)
					currentPosition.Y += childSize.Height;
				else
					currentPosition.X += childSize.Width;
			}

			if (isHorizontal)
			{
				currentPosition.X += Math.Max(0.0, finalSize.Width - _desiredSize.Width);
			}
			else
				currentPosition.Y += Math.Max(0.0, finalSize.Height - _desiredSize.Height);

			foreach (UIElement currentElement in base.InternalChildren)
			{
				if (!GetIsAtStackEnd(currentElement)) continue;

				Size childSize = new Size(finalSize.Width, finalSize.Height);
				if (isHorizontal)
					childSize.Width = (GetAllowNativeSize(currentElement)) ?
						currentElement.DesiredSize.Width : _maxChildLength;
				else
					childSize.Height = (GetAllowNativeSize(currentElement)) ?
						currentElement.DesiredSize.Height : _maxChildLength;

				Rect childBounds = new Rect(currentPosition, childSize);
				currentElement.Arrange(childBounds);

				if (!isHorizontal)
					currentPosition.Y += childSize.Height;
				else
					currentPosition.X += childSize.Width;
			}

			//Keep panel occupying its allocated size.
			return finalSize;
		}
	

	}
}
